/**
 * Copyright (C) 2010 David Simonek <dafesimonek@gmail.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

/*
 * MaterialsTable.java
 *
 * Created on Aug 24, 2010, 10:12:55 AM
 */

package org.dafe.partyband.gui.songs;

import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.text.SimpleDateFormat;
import java.util.*;
import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JComponent;
import javax.swing.JPopupMenu;
import javax.swing.JSeparator;
import javax.swing.KeyStroke;
import javax.swing.ListSelectionModel;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.AbstractTableModel;
import org.dafe.partyband.db.api.ISong;
import org.dafe.partyband.db.api.ISongResource;
import org.dafe.partyband.db.api.handlers.ISongHandler;
import org.dafe.partyband.db.api.handlers.ISongResourceHandler;
import org.dafe.partyband.db.api.DbManager;
import org.dafe.partyband.gui.Utilities;
import org.dafe.partyband.gui.Utilities.CustomColumnFactory;
import org.dafe.partyband.importer.api.IImporterServices;
import org.dafe.partyband.importer.api.IMimeTypeDetector;
import org.jdesktop.swingx.JXTable;
import org.jdesktop.swingx.JXTitledPanel;
import org.jdesktop.swingx.color.ColorUtil;
import org.jdesktop.swingx.decorator.ColorHighlighter;
import org.jdesktop.swingx.decorator.HighlightPredicate;
import org.jdesktop.swingx.decorator.HighlighterFactory;
import org.openide.util.Lookup;
import org.openide.util.Lookup.Result;
import org.openide.util.LookupEvent;
import org.openide.util.LookupListener;
import org.openide.util.NbBundle;
import org.openide.util.lookup.AbstractLookup;
import org.openide.util.lookup.InstanceContent;


/**
 *
 * @author dafe
 */
public class MaterialsTable extends javax.swing.JPanel {

    private static final IMimeTypeDetector mimeDetector = Lookup.getDefault().lookup(IImporterServices.class).getMimeDetector();
    private static final ISongHandler songHandler = DbManager.getBandManager().getHandlersLookup().lookup(ISongHandler.class);
    private static final ISongResourceHandler resHandler = DbManager.getBandManager().getHandlersLookup().lookup(ISongResourceHandler.class);;

    private static final String[] columnNames = new String[] { "preferred", "type", "material", "modified", "size" };
    private static final Class[] columnTypes = new Class [] {
        Boolean.class, String.class, String.class, String.class, String.class
    };

    private final InstanceContent selMaterialsLkpContent;
    private final AbstractLookup selMaterialsLkp;
    private final MaterialsActionsPanel materialsPanel;
    private final MaterialsModel materialsModel;
    private final Result<ISong> curSongLkpResult;



    /** Creates new form MaterialsTable */
    public MaterialsTable(Lookup curSongLkp, final JXTitledPanel titledPanel) {
        initComponents();

        curSongLkpResult = curSongLkp.lookupResult(ISong.class);
        curSongLkpResult.addLookupListener(new LookupListener() {
            public void resultChanged(LookupEvent ev) {
                Collection<? extends ISong> songsRes = curSongLkpResult.allInstances();
                ISong song = songsRes.isEmpty() ? null : songsRes.iterator().next();
                materialsModel.setDataSource(song);
                if (song != null) {
                    titledPanel.setTitle(
                            NbBundle.getMessage(MaterialsTableTitledPanel.class, "MaterialsTableTitledPanel.titlePanel.title",
                            song.getName()));
                }
                if (materialsModel.getRowCount() > 0) {
                    ListSelectionModel materialsSelModel = materialsTable.getSelectionModel();
                    if (materialsSelModel.isSelectionEmpty()) {
                        materialsSelModel.setSelectionInterval(0, 0);
                    }
                }
            }
        });

        materialsTable.addHighlighter(HighlighterFactory.createSimpleStriping());
        materialsTable.addHighlighter(new ColorHighlighter(HighlightPredicate.ROLLOVER_ROW,
                ColorUtil.setAlpha(materialsTable.getSelectionBackground(), 30), null));

        // define column features and properties
        CustomColumnFactory customCF = new Utilities.CustomColumnFactory();
        CustomColumnFactory.ColumnDescription preferredDesc = new CustomColumnFactory.ColumnDescription();
        preferredDesc.title = NbBundle.getMessage(MaterialsTable.class, "TIT_PreferredColumn");
        preferredDesc.tooltip = NbBundle.getMessage(MaterialsTable.class, "HINT_PreferredColumn");
        preferredDesc.cellEditor = new JXTable.BooleanEditor();
        customCF.setColumnDescription(columnNames[0], preferredDesc);
        
        CustomColumnFactory.ColumnDescription typeDesc = new CustomColumnFactory.ColumnDescription();
        typeDesc.title = NbBundle.getMessage(MaterialsTable.class, "TIT_TypeColumn");
        typeDesc.tooltip = NbBundle.getMessage(MaterialsTable.class, "HINT_TypeColumn");
        customCF.setColumnDescription(columnNames[1], typeDesc);
        
        CustomColumnFactory.ColumnDescription materialDesc = new CustomColumnFactory.ColumnDescription();
        materialDesc.title = NbBundle.getMessage(MaterialsTable.class, "TIT_MaterialColumn");
        materialDesc.tooltip = NbBundle.getMessage(MaterialsTable.class, "HINT_MaterialColumn");
        customCF.setColumnDescription(columnNames[2], materialDesc);

        CustomColumnFactory.ColumnDescription modifiedDesc = new CustomColumnFactory.ColumnDescription();
        modifiedDesc.title = NbBundle.getMessage(MaterialsTable.class, "TIT_ModifiedColumn");
        modifiedDesc.tooltip = NbBundle.getMessage(MaterialsTable.class, "HINT_ModifiedColumn");
        customCF.setColumnDescription(columnNames[3], modifiedDesc);
        
        CustomColumnFactory.ColumnDescription sizeDesc = new CustomColumnFactory.ColumnDescription();
        sizeDesc.title = NbBundle.getMessage(MaterialsTable.class, "TIT_MaterialSizeColumn");
        sizeDesc.tooltip = NbBundle.getMessage(MaterialsTable.class, "HINT_MaterialSizeColumn");
        sizeDesc.prototypeDescription = new CustomColumnFactory.PrototypeDescription("7777 Kb", 1.3f, 2.3f);
        customCF.setColumnDescription(columnNames[4], sizeDesc);
        
        materialsTable.setColumnFactory(customCF);

        selMaterialsLkpContent = new InstanceContent();
        selMaterialsLkp = new AbstractLookup(selMaterialsLkpContent);
        materialsModel = new MaterialsModel();
        materialsTable.setModel(materialsModel);
        materialsTable.getSelectionModel().addListSelectionListener(
                new ListSelectionListener() {
            public void valueChanged(ListSelectionEvent e) {
                List<ISongResource> selMaterials = getSelectedMaterials();
                selMaterialsLkpContent.set(Collections.EMPTY_LIST, null);
                for (ISongResource songRes : selMaterials) {
                    selMaterialsLkpContent.add(songRes);
                }
                materialsPanel.setCurMaterials(selMaterials);
            }
        });

        materialsPanel = new MaterialsActionsPanel(selMaterialsLkp, curSongLkp, materialsModel);
        final ActionMap actionMap = materialsTable.getActionMap();
        final InputMap inputMap = materialsTable.getInputMap();
        actionMap.put("OpenMaterialAction", materialsPanel.getOpenAction());
        inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "OpenMaterialAction");
        actionMap.put("DelMaterialAction", materialsPanel.getDeleteAction());
        inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0), "DelMaterialAction");
        actionMap.put("FindMaterialAction", materialsPanel.getFindResourcesAction());
        inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_INSERT, 0), "FindMaterialAction");
        actionMap.put("PlayMp3MaterialAction", materialsPanel.getPlayMp3Action());
        inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0), "PlayMp3MaterialAction");
    }

    public JComponent getMaterialsActionsPanel () {
        return materialsPanel;
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        jScrollPane1 = new javax.swing.JScrollPane();
        materialsTable = new org.jdesktop.swingx.JXTable();

        materialsTable.setShowHorizontalLines(false);
        materialsTable.setShowVerticalLines(false);
        materialsTable.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                materialsTableMouseClicked(evt);
            }
            public void mousePressed(java.awt.event.MouseEvent evt) {
                materialsTableMousePressed(evt);
            }
            public void mouseReleased(java.awt.event.MouseEvent evt) {
                materialsTableMouseReleased(evt);
            }
        });
        jScrollPane1.setViewportView(materialsTable);

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
        this.setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 408, Short.MAX_VALUE)
                .addContainerGap())
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 100, Short.MAX_VALUE)
                .addContainerGap())
        );
    }// </editor-fold>//GEN-END:initComponents

    private void materialsTableMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_materialsTableMouseClicked
        if (evt.getClickCount() == 2) {
            Action openAction = materialsPanel.getOpenAction();
            if (openAction.isEnabled()) {
                openAction.actionPerformed(null);
            }
        }
}//GEN-LAST:event_materialsTableMouseClicked

    private void materialsTableMousePressed(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_materialsTableMousePressed
        showMaterialsPopup(evt);
}//GEN-LAST:event_materialsTableMousePressed

    private void materialsTableMouseReleased(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_materialsTableMouseReleased
        showMaterialsPopup(evt);
}//GEN-LAST:event_materialsTableMouseReleased

    private void showMaterialsPopup (MouseEvent evt) {
        if (!evt.isPopupTrigger()) {
            return;
        }
        Utilities.assureSelected(materialsTable, evt.getPoint());

        JPopupMenu popup = new JPopupMenu();
        Utilities.addAction2Popup(popup, materialsPanel.getOpenAction());
        Utilities.addAction2Popup(popup, materialsPanel.getPlayMp3Action());
        if (popup.getSubElements().length > 0) {
            popup.add(new JSeparator());
        }
        Utilities.addAction2Popup(popup, materialsPanel.getExportAction());
        Utilities.addAction2Popup(popup, materialsPanel.getPrintAction());
        if (popup.getSubElements().length > 0) {
            popup.add(new JSeparator());
        }
        Utilities.addAction2Popup(popup, materialsPanel.getFindResourcesAction());
        Utilities.addAction2Popup(popup, materialsPanel.getAddAction());
        if (popup.getSubElements().length > 0) {
            popup.add(new JSeparator());
        }
        Utilities.addAction2Popup(popup, materialsPanel.getDeleteAction());

        popup.show(materialsTable, evt.getX(), evt.getY());
    }

    public List<ISongResource> getSelectedMaterials() {
        List<ISongResource> result = new ArrayList<ISongResource>();

        int[] selRows = materialsTable.getSelectedRows();
        MaterialsModel model = (MaterialsModel) materialsTable.getModel();
        for (int i = 0; i < selRows.length; i++) {
            result.add(model.getResourceAtRow(materialsTable.convertRowIndexToModel(selRows[i])));
        }

        return result;
    }

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JScrollPane jScrollPane1;
    private org.jdesktop.swingx.JXTable materialsTable;
    // End of variables declaration//GEN-END:variables

    public class MaterialsModel extends AbstractTableModel {

        private SimpleDateFormat sdf = new SimpleDateFormat();
        private ISong song;

        public void setDataSource (ISong song) {
            this.song = song;
            if (song != null) {
                correctPreferredResources();
            }
            fireTableDataChanged();
        }

        public ISong getDataSource () {
            return song;
        }

        public int getRowCount() {
            return song != null ? song.getResources().size() : 0;
        }

        public int getColumnCount() {
            return 5;
        }

        @Override
        public String getColumnName(int column) {
            return columnNames[column];
        }

        @Override
        public Class<?> getColumnClass(int columnIndex) {
            return columnTypes[columnIndex];
        }

        public Object getValueAt(int rowIndex, int columnIndex) {
            if (song == null) {
                return null;
            }
            final ISongResource res = song.getResources().get(rowIndex);
            if (res == null) {
                return null;
            }
            final String mimeType = res.getMimeType();
            switch (columnIndex) {
            case 0:
                return res.isPreferred();
            case 1:
                return mimeDetector.getContentType(mimeType).getDisplayName();
            case 2:
                return mimeDetector.getMimeDisplayName(mimeType) + " (" + mimeType + ")";
            case 3:
                return sdf.format(new Date(res.getLastModified()));
            case 4:
                long length = res.getLength();
                String result;
                if (length > 1024) {
                    result = Math.round(length / 1024d) + " Kb";
                } else {
                    result = length + " b";
                }
                return result;
            }
            throw new IllegalArgumentException("Illegal column index: " + columnIndex);
        }

        @Override
        public boolean isCellEditable(int rowIndex, int columnIndex) {
            return columnIndex == 0;
        }

        @Override
        public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
            assert columnIndex == 0;
            
            // assure that only one resource of each type is preferred
            Boolean prefValue = (Boolean)aValue;
            final ISongResource res = song.getResources().get(rowIndex);
            ISongResource prefRes = Utilities.getPreferredResourceOfType(song, 
                    mimeDetector.getContentType(res.getMimeType()));
            
            if (!res.equals(prefRes)) {
                if (prefRes != null) {
                    prefRes.setPreferred(!prefValue);
                    resHandler.storeWithoutBlob(prefRes);
                }
                res.setPreferred(prefValue);
                resHandler.storeWithoutBlob(res);
            }
        }

        public ISongResource getResourceAtRow(int rowIndex) {
            return song.getResources().get(rowIndex);
        }

        public void deleteResource (ISongResource songRes) {
            songHandler.removeResource(song, songRes);
            fireTableDataChanged();
            DbManager.getObjectContainer().commit();
        }

        public void addResource (ISongResource songRes, boolean importData) {
            songHandler.addResource(song, songRes, importData);
            fireTableDataChanged();
            DbManager.getObjectContainer().commit();
        }

        private void correctPreferredResources() {
            for (IMimeTypeDetector.ContentType ct : IMimeTypeDetector.ContentType.values()) {
                ISongResource prefRes = Utilities.getPreferredResourceOfType(song, ct);
                if (prefRes != null && !prefRes.isPreferred()) {
                    prefRes.setPreferred(true);
                    resHandler.storeWithoutBlob(prefRes);
                }
            }
        }

    }

}
